﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using Microsoft.Xna.Framework;
using HexChessBoard;

namespace HexChessBoard.PlayerComponents
{
    class PlayerAI : Player
    {
        public PlayerAI(Board board, PieceColor pieceColor)
            : base(board, pieceColor)
        { }

        public override void Draw(GameTime gameTime)
        {
            //throw new NotImplementedException();
        }

        public override void Update(Board board, GameTime gameTime)
        {
            // todo branch into separate process, asynchronous thread, blink brain when thinking
            // Get the best available move
            PlayerBoard.isAIThinking = true;
            Board compareBoard = new Board(board);
            bestMove = new Move();
            bestMove.Score = int.MinValue;
            //ar = dlgt.BeginInvoke(PlayerBoard, compareBoard, bestMove, (int)PlayerBoard.difficulty, CallbackMethod, dlgt);// fire the asynch call

            int depth = (int)PlayerBoard.difficulty;
            List<Move> possibleMoves = Evaluation.GetPossibleMoves(compareBoard, ref depth);
//            numberActiveThreads = 0;

//#if XBOX
//            // Construct items for XBox parallel processing
//            int threadID = 1;
//            List<Move> SecondaryMoves = new List<Move>(20);
//            numberActiveThreads = (int)Math.Ceiling((double)possibleMoves.Count / 20);     // The number of processes we will spawn

//            for (int i = 0; i < possibleMoves.Count; i++)
//            {
//                // Add available moves
//                SecondaryMoves.Add(possibleMoves[i]);

//                // If we've grabbed ten moves, or however many are left when count is less than 10
//                if ((i != 0 || possibleMoves.Count == 1) && (i % 19 == 0 || i == possibleMoves.Count - 1))
//                {
//                    Board b = new Board(compareBoard);
//                    List<Move> TertiaryMoves = new List<Move>(20);
//                    for (int j = 0; j < SecondaryMoves.Count; j++)
//                        TertiaryMoves.Add(new Move(SecondaryMoves[j].SourceTilePiece, SecondaryMoves[j].SourceTileSide, SecondaryMoves[j].SourceTileIndex, SecondaryMoves[j].DestTileIndex, SecondaryMoves[j].DestTilePiece, SecondaryMoves[j].WasEnPassant, SecondaryMoves[j].EnPassantIndex, SecondaryMoves[j].SourceTilePieceHasMoved, SecondaryMoves[j].SourceTilePieceWasPawnOnStartingTile, SecondaryMoves[j].BoardRepeatCount, SecondaryMoves[j].BoardFiftyMoveCount, SecondaryMoves[j].Score));

//                    //Thread.CurrentThread.SetAffinity(threadID);
//                    int[] threadsToUse = new int[] { threadID };
//                    Thread.CurrentThread.SetProcessorAffinity(threadsToUse);
//                    System.Threading.ThreadPool.QueueUserWorkItem(Func =>
//                    {
//                        //T result = ReadObject<T>(filename);
//                        //callback(result);
//                        GetBestMove(PlayerBoard, b, depth, TertiaryMoves);
//                        //ManualResetEvent e = new ManualResetEvent(false);
//                    });

//                    // Clear moves for next round
//                    SecondaryMoves.Clear();

//                    // Advance thread affinity, running on cores 1, 3, 4, 5
//                    if (threadID == 1)
//                        threadID = 3;
//                    else if (threadID == 3)
//                        threadID = 4;
//                    else if (threadID == 4)
//                        threadID = 5;
//                    else
//                        threadID = 5;
//                }
//            }
//#else
//            numberActiveThreads++;
            System.Threading.ThreadPool.QueueUserWorkItem(Func =>
                {
                    //T result = ReadObject<T>(filename);
                    //callback(result);
                    GetBestMove(PlayerBoard, compareBoard, depth, possibleMoves);
                });
//#endif

            SelectedTile = null;
            SourceTile = null;
        }

        //public override void HandleInput(InputState inputState)
        //{
            //throw new NotImplementedException();
        //}

        public override void HandleInput(InputState inputState, GameTime gameTime)
        {
            //throw new NotImplementedException();
        } 

        static Move bestMove = new Move();
        //static int numberActiveThreads = 0;
        //AsyncDelegate dlgt = new AsyncDelegate(GetBestMove);
        //IAsyncResult ar;
        public delegate void AsyncDelegate(Board playerBoard, Board compareBoard, int depth);
        static private void GetBestMove(Board playerBoard, Board compareBoard, int depth, List<Move> possibleMoves)
        {
            DateTime startTime = DateTime.Now;

            Move localBestMove = Evaluation.AlphaBetaRoot(compareBoard, (int)playerBoard.difficulty, possibleMoves);
            if (localBestMove.Score > bestMove.Score)
            {
                bestMove = localBestMove;
            }
            
            DateTime endTime = DateTime.Now;
            TimeSpan ts = endTime - startTime;

            if (ts.Seconds < 2)
                System.Threading.Thread.Sleep(1000);

            //numberActiveThreads--;

            // Last thread to exit executes the best move
            // Check if disposed so if player exits during thinking, does not crash with disposed objects
            //if (numberActiveThreads == 0 && !playerBoard.moveSound.IsDisposed)
            if (!playerBoard.moveSound.IsDisposed)
            {
                // Move the piece
                if (!playerBoard.BlackCheckmate && !playerBoard.WhiteCheckmate && !playerBoard.Stalemate)
                {
                    if ((playerBoard.Tiles[bestMove.SourceTileIndex].WasEnPassant && playerBoard.Tiles[bestMove.SourceTileIndex].Piece == PieceType.Pawn && playerBoard.Tiles[bestMove.SourceTileIndex].file != playerBoard.Tiles[bestMove.DestTileIndex].file) || playerBoard.Tiles[bestMove.DestTileIndex].Piece != PieceType.None)
                        playerBoard.captureSound.Play();
                    else
                        playerBoard.moveSound.Play();

                    if (playerBoard.FiftyMoveCount == 50 || playerBoard.FiftyMoveCount == 75 || playerBoard.FiftyMoveCount > 94 || playerBoard.RepeatedMoveCount == 2 || playerBoard.RepeatedMoveCount == 3)
                        playerBoard.timerSound.Play();
                }
                // clear repeated move amounts to reduce overhead
                // Capture pieces from opposing sides
                if (playerBoard.Tiles[bestMove.DestTileIndex].Piece != PieceType.None || (playerBoard.Tiles[bestMove.DestTileIndex].Piece == PieceType.None && playerBoard.Tiles[bestMove.SourceTileIndex].Piece == PieceType.Pawn && playerBoard.Tiles[bestMove.SourceTileIndex].WasEnPassant))
                    playerBoard.BoardPositions.Clear();
                // Move the piece
                BoardSupport.MovePiece(ref playerBoard, ref playerBoard.Tiles[bestMove.SourceTileIndex], ref playerBoard.Tiles[bestMove.DestTileIndex]);
                // End the turn
                BoardSupport.EndTurn(ref playerBoard, PieceType.Queen);

                if (playerBoard.BlackCheckmate || playerBoard.WhiteCheckmate || playerBoard.Stalemate)
                    playerBoard.endSound.Play();
                else if (playerBoard.WhiteCheck || playerBoard.BlackCheck)
                    playerBoard.checkSound.Play();

                playerBoard.isAIThinking = false;
            }

            // -----------------------------------
            // OLD
            // -----------------------------------
            //Console.WriteLine("Test Method Begins: ");
            //Thread.Sleep(callDuration);
            //info = 1;
            //return "MyCallTime was " + callDuration.ToString();
        }
        static void CallbackMethod(IAsyncResult ar)
        {

            // -----------------------------------
            // OLD
            // -----------------------------------
            //Console.WriteLine("Callback fired.");

            //// Retrieve the delegate.
            //AsyncDelegate dlgt = (AsyncDelegate)ar.AsyncState;

            //// Call EndInvoke to retrieve the results.
            //string ret = dlgt.EndInvoke(out threadID, ar);

            //Console.WriteLine("The call executed on thread {0}, with return value \"{1}\".", threadID, ret);
        }
    }
}
